//////////////////////////////////////////////////
// EASIM project
// easim.draw.FigPainter
// 6/7/2007 5:49 PM
// Cong Liu
// gzcong@gmail.com

// Copyright (C) 2007
// All rights reserved.
// Refer to LICENSE of JIST (Java In Simulation Time) for terms and conditions of use.

package easim.draw;

import java.awt.image.BufferedImage;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;

public class FigPainter implements jist.runtime.JistAPI.DoNotRewrite
{

	private static Graphics2D graphics2D;
  private StringBuffer buf;
  private int depth = 800;

	private static FontMetrics getFontMetrics(Font font) {
		if (graphics2D == null) {
			BufferedImage bufferedImage = new BufferedImage(
				2, 2, BufferedImage.TYPE_4BYTE_ABGR_PRE);
			graphics2D = (Graphics2D)(bufferedImage.createGraphics());
		}
		return graphics2D.getFontMetrics(font);
	}

  public FigPainter() {
    buf = new StringBuffer();
    String header =
			"#FIG 3.2\r\n" + 
			"Portrait\r\n" + 
			"Center\r\n" + 
			"Metric\r\n" + 
			"A4\r\n" + 
			"100.0\r\n" + 
			"Single\r\n" + 
			"-2\r\n" + 
			"80 2\r\n";
    buf.append(header);
  }

	public void paint(Field field) {
    Iterator it = field.links.keySet().iterator();
    while (it.hasNext()) {
		  String link = (String)it.next();
			String[] nodes = link.split("-");
			paint((Link)field.links.get(link), 
        (Node)field.nodes.get(nodes[0]), (Node)field.nodes.get(nodes[1]));
		}
		Node[] nodes = (Node[])field.nodes.values().toArray(new Node[0]);
		sortNodes(nodes);
		for (int i = 0; i < nodes.length; ++ i) {
			paint(nodes[i], null);
		}
	}

	public void paint(Link link, Node node1, Node node2) {
		int dx = node1.x - node2.x;
		int dy = node1.y - node2.y;
    if (dx < 0) dx = -dx;
    if (dy < 0) dy = -dy;
		double length = Math.sqrt(dx * dx + dy * dy);
    if (! (length > node1.size + node1.thickness + node2.size + node2.thickness)) {
      return;
    }
    dx = node1.x - node2.x;
    dy = node1.y - node2.y;
    int x1 = node1.x - (int)(dx * (node1.size + node1.thickness) / length);
    int y1 = node1.y - (int)(dy * (node1.size + node1.thickness) / length);
    int x2 = node2.x + (int)(dx * (node2.size + node2.thickness) / length);
    int y2 = node2.y + (int)(dy * (node2.size + node2.thickness) / length);
    Line line = new Line(x1, y1, x2, y2);
    if (link.lineStyle.equals(Setting.LineStyle.SOLID_LINE.toString())) {
      line.line_style = 0;
    }
    else if (link.lineStyle.equals(Setting.LineStyle.DASH_LINE.toString())) {
      line.line_style = 1;
    }
    else if (link.lineStyle.equals(Setting.LineStyle.DOTTED_LINE.toString())) {
      line.line_style = 2;
    }
    if (link.arrow.equals("ARROW1") || link.arrow.equals("ARROW_BOTH")) {
      line.forward_arrow = 1;
    }
    if (link.arrow.equals("ARROW2") || link.arrow.equals("ARROW_BOTH")) {
      line.backward_arrow = 1;
    }
    line.thickness = link.thickness;
    line.depth = depth;
    -- depth;
    if (depth < 1) depth = 1;
    buf.append(line.toString() + "\r\n");

		if (link.label != null && link.label.trim().length() != 0) {
			int fontSize = node1.size * 3 / 2;
			if (fontSize > node2.size * 3 / 2) fontSize = node2.size * 3 / 2;
      if (fontSize > 15) fontSize = 15;
			Font font = new Font(Setting.FONT, Font.PLAIN, fontSize);
			FontMetrics fontMetrics = getFontMetrics(font);
      String[] text = link.label.split("\\\\n");
      int width = 0;
      int height = fontSize * text.length;
      for (int i = 0; i < text.length; ++ i) {
        int w = fontMetrics.stringWidth(text[i]);
        if (width < w) width = w;
      }
      dx = x1 - x2;
      dy = y1 - y2;
      if (dx == 0 && dy == 0) return;
      int x = (x1 + x2) / 2;
      int y = (y1 + y2) / 2;
      int gap = link.thickness / 2 + 1;
      double len = Math.sqrt(width * width + height * height) / 2;
      double len2 = Math.sqrt(dx * dx + dy * dy);
      len += gap;
      if (dx == 0) {
        if (dy > 0) {
          x += width / 2 + gap;
        }
        else {
          x -= width / 2 + gap;
        }
      }
      else if (dy == 0) {
        if (dx > 0) {
          y -= height / 2 + gap;
        }
        else {
          y += height / 2 + gap;
        }
      }
      else {
        if (dx > 0 && dy > 0) {
          double tan1 = - dx / (double)dy;
          double tan2 = - height / (double)width;
          double tan12 = (tan1 - tan2)/(1 + tan1 * tan2);
          double alpha = Math.atan(tan12);
          double dis = len * Math.cos(alpha);
          x += (int)(dis * (dy / len2));
          y -= (int)(dis * (dx / len2));
        }
        else if (dx > 0 && dy < 0) {
          double tan1 = - dx / (double)dy;
          double tan2 = height / (double)width;
          double tan12 = (tan1 - tan2)/(1 + tan1 * tan2);
          double alpha = Math.atan(tan12);
          double dis = len * Math.cos(alpha);
          x -= (int)(dis * (-dy / len2));
          y -= (int)(dis * (dx / len2));
        }
        else if (dx < 0 && dy < 0) {
          double tan1 = - dx / (double)dy;
          double tan2 = - height / (double)width;
          double tan12 = (tan1 - tan2)/(1 + tan1 * tan2);
          double alpha = Math.atan(tan12);
          double dis = len * Math.cos(alpha);
          x += (int)(dis * (dy / len2));
          y -= (int)(dis * (dx / len2));
        }
        else if (dx < 0 && dy > 0) {
          double tan1 = - dx / (double)dy;
          double tan2 = height / (double)width;
          double tan12 = (tan1 - tan2)/(1 + tan1 * tan2);
          double alpha = Math.atan(tan12);
          double dis = len * Math.cos(alpha);
          x += (int)(dis * (dy / len2));
          y += (int)(dis * (-dx / len2));
        }
      }
      x = x - width / 2 + (int)(fontSize * 0.01);
      y = y - height / 2 - (int)(fontSize * 0.15);
      for (int i = 0; i < text.length; ++ i) {
        Text text1 = new Text(x, y + (i + 1) * fontSize, text[i], fontSize, width, depth);
        -- depth;
        if (depth < 1) depth = 1;
        buf.append(text1.toString() + "\r\n");
      }
		}
  }

	public void paint(Node node, Color color) {
		int size2 = node.size + node.thickness / 2;
    Oval oval = new Oval(node.x, node.y, size2);
    oval.thickness = node.thickness;
    if (node.lineStyle.equals(Setting.LineStyle.SOLID_LINE.toString())) {
      oval.line_style = 0;
    }
    else if (node.lineStyle.equals(Setting.LineStyle.DASH_LINE.toString())) {
      oval.line_style = 1;
    }
    else if (node.lineStyle.equals(Setting.LineStyle.DOTTED_LINE.toString())) {
      oval.line_style = 2;
    }
    oval.depth = depth;
    -- depth;
    if (depth < 1) depth = 1;
		oval.fill_color = 7; // white
		oval.area_fill = 20;
    buf.append(oval.toString() + "\r\n");
    String[] text = node.label.split("\\\\n");
		if (node.label != null && node.label.trim().length() != 0) {
			int fontSize = node.size * 2 / text.length;
			while (fontSize >= 8) {
				Font font = new Font(Setting.FONT, Font.PLAIN, fontSize);
				FontMetrics fontMetrics = getFontMetrics(font);
				int fontWidth = 0;
        for (int i = 0; i < text.length; ++ i) {
          int width = fontMetrics.stringWidth(text[i]);
          if (width > fontWidth) fontWidth = width;
        }
        float width = (float)(fontWidth);
        float height = (float)(fontSize * text.length);
				if (width * width + height * height > node.size * node.size * 4) {
          double times = (width * width + height * height) / (double)(node.size * node.size * 4);
          times = Math.sqrt(times);
					int newSize = (int)(fontSize / times);
          if (newSize < fontSize) {
            fontSize = newSize;
          }
          else {
            -- fontSize;
          }
					continue;
				}
        //float factor = 1.1f;
        //width *= factor;
        //height *= factor;
        //fontSize *= factor;
        int x = node.x - (int)(width / 2);
        int y = node.y - (int)(height / 2) - (int)(fontSize * 0.22);
        for (int i = 0; i < text.length; ++ i) {
			    Text obj = new Text(x, y + (i + 1) * fontSize, text[i], fontSize, fontWidth, depth);
          -- depth;
          if (depth < 1) depth = 1;
          buf.append(obj + "\r\n");
        }
				break;
			}
		}

	}

  public static void sortNodes(Node[] nodes) {
		Arrays.sort(nodes, new Comparator() {
			public int compare(Object node01, Object node02) {
        Node node1 = (Node)node01;
        Node node2 = (Node)node02;
				return node1.id.compareTo(node2.id);
			}
		});
	}

  public String toString() {
    return buf.toString();
  }

}

